/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package test;

import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import parameters.Parameters;
import semantics.Run;
import semantics.RunStep;
import semantics.State;
import sun.misc.JavaAWTAccess;
import syntax.ClockGuard;
import syntax.Location;
import syntax.TIOA;
import syntax.Transition;

/**
 *
 * @author hicham
 */
public class MinDelayTestStrategy extends AllTransitionsTestStrategy {

    /*Main method to test all transitions*/
    /**
     * 
     * @param tioa
     * @return a string reprsenting the result of the test
     */
    @Override
    public String test(TIOA tioa) {
        Run minRun = new Run();
        StringBuilder stb = new StringBuilder();
        State initState = new State();
        minRun.setLastState(initState);
        initState.initializeState(tioa.getRepository(), tioa.getInitialLocation());
        int size = tioa.getRepository().getAllLocations().size();
        List<Transition> transitions = new LinkedList<Transition>(tioa.getRepository().getAllTransitions());
        Collections.sort(transitions);
        for (Transition trans : transitions) {
            stb.append(testTransition(tioa, trans));
        }
        return stb.toString();
    }

    /*Methode to test one single transition*/
    /**
     * 
     * @param tioa : the tioa to be tested
     * @param transition : the transition to be tested
     * @return 
     */
    public String testTransition(TIOA tioa, Transition transition) {
        State bestMinState;
        State stateAfterTest;
        State currState;
        State lastState;

        StringBuilder stb = new StringBuilder();
        stb.append(transition);

        //getting the preamble to the source of transition
        bestMinState = getPreamble(tioa, transition);

        //generating the test case
        stateAfterTest = generateTransitionTest(tioa, transition, bestMinState);

        //generating the postambule
        lastState = getPostambule(tioa, stateAfterTest);

        State initState = new State();
        initState.initializeState(tioa.getRepository(), tioa.getInitialLocation());

        stb.append("\nPREAMBLE\n");
        stb.append(getRunSteps(initState, bestMinState));

//        stb.append(generateTransitionTest(tioa, transition, bestMinState));
//        stb.append(getTestCase(initState, bestMinState));
        stb.append("\nTEST\n");
        stb.append(stateAfterTest);
        stb.append("\nPOSTAMBLE\n");
        if (lastState == null) {
            stb.append("\nPOSTAMBLE UNDEFINED\n");
        } else {
            stb.append(getRunSteps(stateAfterTest, lastState));
        }
        stb.append("\n==================\n");

        return stb.toString();
    }

    /*Method for generating a transition test
     * Input : The tioa
     * Input : the transition for which a test must be generated
     * Input : the state obtained after a best delay preamble for which the transition is executable
     */
    /**
     * @param tioa
     * @param trans
     * @param bestMinState
     * @return 
     */
    public State generateTransitionTest(TIOA tioa, Transition trans, State bestMinState) {
        State res = null;
        State srcState = bestMinState;
        float delay = srcState.getDelay(trans);
        State newState = srcState.computeNewState(trans, delay);
        return newState;
    }

    /*A method to compute the best path leading from a source to a transition*/
    /***
     * 
     * @param tioa
     * @param srcState
     * @param transition
     * @return 
     */
    public State getMinPathFromStateToTransition(TIOA tioa, State srcState, Transition transition) {
        float curDelay, bestDelay, infinity;
        Stack<State> statesToHandle = new Stack();
        State currState, bestMinState = null;
        Set<State> handledStates = new HashSet<State>();
        boolean checkFireness;
        Location dest = transition.getSrcLocation();
        ClockGuard clockGuard = transition.getClockGuard();

        curDelay = srcState.getBestDelay();
        infinity = Parameters.getInfinity() + 1;
        bestDelay = infinity;

        statesToHandle.push(srcState);
        Location curLoc;

        while (!statesToHandle.empty()) {
            currState = statesToHandle.pop();
            checkFireness = currState.isExecutable(transition);
            handledStates.add(currState);
            curDelay = currState.getBestDelay();
            Location src = currState.getLocation();
            if (src.equals(dest) && (checkFireness)) {
                if (curDelay <= bestDelay) {
                    bestDelay = curDelay;
                    bestMinState = currState;
                    //TODO: Runsteps
                } else {
                    continue;
                }
            } else {
                for (Transition trans : src.getOutgoingTrans()) {
                    float transDelay = currState.getDelay(trans);
                    curDelay = currState.getBestDelay() + transDelay;

                    if (curDelay >= bestDelay) {
                        continue;
                    } else {
                        //TODO: RunSteps
                        if (!currState.isExecutable(trans)) {
                            continue;
                        }

                        State newState = currState.computeNewState(trans, transDelay);

                        newState.setBestDelay(curDelay);
                        RunStep runStep = new RunStep(currState, transDelay, trans);
                        newState.setLastRunStep(runStep);
                        if (!handledStates.contains(newState)) {
                            statesToHandle.push(newState);
                        }
                    }
                }
            }

        }
        currState = bestMinState;
        System.out.println(currState);
        if (bestMinState == null) {
            return null;
        }
        while (currState.getLastRunStep() != null && (!currState.equals(srcState))) {
            System.out.print(currState.getLastRunStep());
            currState = currState.getLastRunStep().getSrcState();
        }
        return bestMinState;

    }

    /*Main method to compute the preamble : a wrapper to a transition*/
    /***
     * @param tioa
     * @param transition
     * @return 
     */
    @Override
    public State getPreamble(TIOA tioa, Transition transition) {
        State srcState = new State();
        srcState.initializeState(tioa.getRepository(), tioa.getInitialLocation());
        return getMinPathFromStateToTransition(tioa, srcState, transition);
    }

    /***
     * 
     */
    public MinDelayTestStrategy() {
    }

    /****
     * @param tioa
     * @param state
     * @return 
     */
    @Override
    public State getPostambule(TIOA tioa, State state) {
        Transition ficTrans = tioa.getFictiousTransition();
        State lastState = getMinPathFromStateToTransition(tioa, state, ficTrans);
        return lastState;
    }

    /******************
     * Private methods
     ******************/
    
//    /***
//     * Method for getting a delay
//     * @param trans
//     * @param state
//     * @return 
//     */    
//    private float getDelay(Transition trans, State state) {
//        float res = 0f;
//        if (trans.getMessage().isOutput()) {
//            res = trans.maxDelay(state);
//        } else {
//            res = trans.minDelay(state);
//        }
//        return res;
//    }

    /***
     * Method for iterating over a run in the inverse order
     * @param src
     * @param dest
     * @return 
     */
    private String getRunSteps(State src, State dest) {
        Stack<RunStep> states = new Stack<RunStep>();
        StringBuilder stb = new StringBuilder();
        State currState = dest;
        while (currState.getLastRunStep() != null && (!currState.equals(src))) {
            states.push(currState.getLastRunStep());
            currState = currState.getLastRunStep().getSrcState();
        }

        while (!states.empty()) {
            RunStep runStep = states.pop();
            stb.append(runStep);
        }

        return stb.toString();
    }
}
